package vg.services.user_interface_manager.additional_swing_components.conditionPanel;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

import vg.services.main_manager.VGMainServiceHelper;
import vg.services.user_interface_manager.additional_swing_components.conditionPanel.components.ICondition;
import vg.services.user_interface_manager.additional_swing_components.conditionPanel.components.RealCondition;
import vg.services.user_interface_manager.additional_swing_components.conditionPanel.components.StringCondition;
import vg.services.user_interface_manager.additional_swing_components.conditionPanel.components.Operator;
import vg.services.user_interface_manager.additional_swing_components.conditionPanel.data.ConditionToken;

public class ConditionalPanel {
	// Defines
	public static final int DEF_VERTEX_TYPE = 1;
	public static final int DEF_EDGE_TYPE = 2;

	// Swing components
    private JPanel view;
	private JPanel conditionalPanel;
	private JScrollPane conditionalPanelScroll; 
	private JButton addStringConditionButton, addRealConditionButton;
	private List<ICondition> conditions;
	private List<Operator> operators;

	// Data
	private Set<String> stringAttributeSet;
	private Set<String> realAttributeSet;

	// Mutex
	private final Object generalMutex = new Object();

    /**
	 * Constructor.
	 */
	public ConditionalPanel() {
		init();
	}
	
	/**
	 * This method clears existing conditions and sets collection of attributes for conditions panel.
     */
	public void setAttributeSets(Set<String> stringAttributeSet, Set<String> realAttributeSet) {
		synchronized (generalMutex) {
			if(stringAttributeSet == null)
				this.stringAttributeSet = Collections.emptySet();
			else	
				this.stringAttributeSet = stringAttributeSet;

            if(realAttributeSet == null)
				this.realAttributeSet = Collections.emptySet();
			else
				this.realAttributeSet = realAttributeSet;

            conditions.clear();
            operators.clear();

            rebuildView();
		}
	}
	
	public void updateUITheme() {
	    rebuildView();
    }

	public Set<String> getStringAttributeSet() {
		synchronized (generalMutex) {
			return Collections.unmodifiableSet(stringAttributeSet);
		}
	}
	
	public Set<String> getRealAttributeSet() {
		return Collections.unmodifiableSet(realAttributeSet);
	}
	
	/**
	 * This method returns view of this component.
	 */
	public JPanel getView() {
		synchronized (generalMutex) {
            return view;
        }
	}

    /**
     * Order : attribute name, sign of comparison, value
     */
	public List<ConditionToken> getConditionTokens() {
		synchronized (generalMutex) {
            List<ConditionToken> tokens = new ArrayList<ConditionToken>();

            Iterator<Operator> operatorIterator = operators.iterator();
            Iterator<ICondition> conditionIterator = conditions.iterator();
            while (conditionIterator.hasNext()) {
                ICondition condition = conditionIterator.next();

                if (condition instanceof StringCondition) {
                    StringCondition stringCondition = (StringCondition)condition;
                    tokens.add(new ConditionToken(stringCondition.getLeftBracket(), ConditionToken.BRACKET_TOKEN_TYPE));
                    tokens.add(new ConditionToken(stringCondition.getName(), ConditionToken.STRING_ATTRIBUTE_NAME_TOKEN_TYPE));
                    tokens.add(new ConditionToken("=", ConditionToken.COMPARISON_TOKEN_TYPE));
                    tokens.add(new ConditionToken(stringCondition.getValue(), ConditionToken.STRING_ATTRIBUTE_VALUE_TOKEN_TYPE));
                    tokens.add(new ConditionToken(stringCondition.getRightBracket(), ConditionToken.BRACKET_TOKEN_TYPE));
                } else if (condition instanceof RealCondition) {
                    RealCondition realCondition = (RealCondition)condition;
                    tokens.add(new ConditionToken(realCondition.getLeftBracket(), ConditionToken.BRACKET_TOKEN_TYPE));
                    tokens.add(new ConditionToken(realCondition.getName(), ConditionToken.REAL_ATTRIBUTE_NAME_TOKEN_TYPE));
                    tokens.add(new ConditionToken(">=", ConditionToken.COMPARISON_TOKEN_TYPE));
                    tokens.add(new ConditionToken(realCondition.getLeftValue(), ConditionToken.REAL_ATTRIBUTE_VALUE_TOKEN_TYPE));
                    tokens.add(new ConditionToken("and", ConditionToken.OPERATION_TOKEN_TYPE));
                    tokens.add(new ConditionToken(realCondition.getName(), ConditionToken.REAL_ATTRIBUTE_NAME_TOKEN_TYPE));
                    tokens.add(new ConditionToken("<=", ConditionToken.COMPARISON_TOKEN_TYPE));
                    tokens.add(new ConditionToken(realCondition.getRightValue(), ConditionToken.REAL_ATTRIBUTE_VALUE_TOKEN_TYPE));
                    tokens.add(new ConditionToken(realCondition.getRightBracket(), ConditionToken.BRACKET_TOKEN_TYPE));
                }

                if (operatorIterator.hasNext() && conditionIterator.hasNext()) {
                    Operator operator = operatorIterator.next();

                    tokens.add(new ConditionToken(operator.getText(), ConditionToken.OPERATION_TOKEN_TYPE));
                }
            }

            return tokens;
        }
	}

//==============================================================================
//---------------PRIVATE METHODS------------------------------------------------
	private void rebuildView() {
		if (!SwingUtilities.isEventDispatchThread()) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    rebuildView();
                }
            });
        } else {
            synchronized (generalMutex) {
                //clear panel
                conditionalPanel.removeAll();

                //create gbc parameter
                GridBagConstraints gbc = null;

                //add all conditions to panel
                for(int i = 0; i < conditions.size(); i++) {
                    // add condition
                    gbc = new GridBagConstraints(0,2*i,  1,1,  1,0,  GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(3, 3, 3, 0),  0,0);
                    conditionalPanel.add(conditions.get(i).getView(), gbc);
                }
                for(int i = 0; i < operators.size() - 1; i++) {
                    gbc = new GridBagConstraints(0,2*i + 1,  5,1,  0,0,  GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(3, 3, 3, 3),  0,0);
                    conditionalPanel.add(operators.get(i).getView(), gbc);
                }

                //add plus button to panel
                conditionalPanel.add(addStringConditionButton, new GridBagConstraints(0,2*conditions.size() + 1,  5,1,  1,1,  GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5),  0,0));
                conditionalPanel.add(addRealConditionButton, new GridBagConstraints(0,2*conditions.size() + 2,  5,1,  1,1,  GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5),  0,0));

                // rebuildView ui
                view.updateUI();
            }
        }
	}

    private void init() {
        if (!SwingUtilities.isEventDispatchThread()) {
            try {
                SwingUtilities.invokeAndWait(new Runnable() {
                    @Override
                    public void run() {
                        init();
                    }
                });
            } catch (Exception ex) {
                VGMainServiceHelper.logger.printException(ex);
            }
        } else {
            synchronized (generalMutex) {
                conditions = new ArrayList<ICondition>();
                operators = new ArrayList<Operator>();

                view = new JPanel(new GridLayout(1, 1));
                conditionalPanel = new JPanel(new GridBagLayout());
                conditionalPanelScroll = new JScrollPane(conditionalPanel);
                view.add(conditionalPanelScroll);

                addStringConditionButton = new JButton("new string condition");
                addRealConditionButton = new JButton("new real condition");
                addStringConditionButton.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        synchronized (generalMutex) {
                            final ICondition condition = new StringCondition(stringAttributeSet);
                            final Operator operator = new Operator();

                            condition.addCloseListener(new ActionListener() {
                                @Override
                                public void actionPerformed(ActionEvent e) {
                                    synchronized (generalMutex) {
                                        conditions.remove(condition);
                                        operators.remove(operator);
                                        rebuildView();
                                    }
                                }
                            });
                            conditions.add(condition);
                            operators.add(operator);
                            rebuildView();
                        }
                    }
                });

                addRealConditionButton.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        synchronized (generalMutex) {
                            final ICondition condition = new RealCondition(realAttributeSet);
                            final Operator operator = new Operator();

                            condition.addCloseListener(new ActionListener() {
                                @Override
                                public void actionPerformed(ActionEvent e) {
                                    synchronized (generalMutex) {
                                        conditions.remove(condition);
                                        operators.remove(operator);
                                        rebuildView();
                                    }
                                }
                            });
                            conditions.add(condition);
                            operators.add(operator);
                            rebuildView();
                        }
                    }
                });
            }
        }
    }
}
